class JSsandbox {
    constructor() {
        // 存储每个应用的沙箱环境
        this.sandboxes = new Map()
        // 存储当前运行的应用
        this.currentRunningApp = null
        // 不需要代理的全局属性
        this.excludeKeys = ['webpackJsonp', 'window', 'document', 'location', 'top', 'history']
    }

    // 创建沙箱环境
    createSandbox(appName) {
        const sandbox = {
            active: false,
            // 存储修改过的全局变量
            modifyPropsMap: {},
            // 代理对象
            proxy: null,
            // Shadow DOM 容器
            shadowRoot: null
        }

        // 创建一个隔离的 DOM 环境
        const container = document.createElement('div')
        container.id = `${appName}-js-sandbox`
        document.body.appendChild(container)
        sandbox.shadowRoot = container.attachShadow({ mode: 'closed' })

        // 使用应用名称创建唯一的上下文变量名
        const contextName = `__SANDBOX_CONTEXT_${appName.replace(/-/g, '_')}__`

        // 在 Shadow DOM 中创建一个新的 script 上下文
        const scriptContext = document.createElement('script')
        scriptContext.textContent = `
            // 创建一个新的上下文对象
            window['${contextName}'] = {
                window: {},
                document: document,
                location: location,
                history: history
            };
        `
        sandbox.shadowRoot.appendChild(scriptContext)

        const baseHandler = {
            get: (target, prop) => {
                // 优先从修改的属性中获取
                if (sandbox.modifyPropsMap.hasOwnProperty(prop)) {
                    return sandbox.modifyPropsMap[prop]
                }
                // 对于 DOM 操作相关的属性，重定向到 Shadow DOM
                if (prop === 'document') {
                    return sandbox.shadowRoot
                }
                // 从应用特定的上下文中获取
                if (window[contextName] && window[contextName][prop] !== undefined) {
                    return window[contextName][prop]
                }
                // 否则从原始 window 对象获取
                const value = window[prop]
                return typeof value === 'function' ? value.bind(window) : value
            },
            set: (target, prop, value) => {
                // 如果是不可修改的属性，直接返回
                if (this.excludeKeys.includes(prop)) {
                    return true
                }
                // 存储修改的值到应用特定的上下文
                if (!window[contextName]) {
                    window[contextName] = {}
                }
                window[contextName][prop] = value
                sandbox.modifyPropsMap[prop] = value
                return true
            },
            has: (target, prop) => {
                return prop in window || prop in sandbox.modifyPropsMap
            }
        }

        // 创建代理对象
        sandbox.proxy = new Proxy(window, baseHandler)
        this.sandboxes.set(appName, sandbox)
        return sandbox
    }

    // 激活沙箱
    active(appName) {
        let sandbox = this.sandboxes.get(appName)
        if (!sandbox) {
            sandbox = this.createSandbox(appName)
        }

        sandbox.active = true
        this.currentRunningApp = appName

        // 将代理对象挂载到全局
        window.__POWERED_BY_QIANKUN__ = true
        window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ = sandbox.proxy
        
        // 返回沙箱环境
        return sandbox.proxy
    }

    // 失活沙箱
    deactive(appName) {
        const sandbox = this.sandboxes.get(appName)
        if (!sandbox) return

        sandbox.active = false
        this.currentRunningApp = null

        // 清理 Shadow DOM 内容
        if (sandbox.shadowRoot) {
            while (sandbox.shadowRoot.firstChild) {
                sandbox.shadowRoot.removeChild(sandbox.shadowRoot.firstChild)
            }
        }

        // 清理应用特定的上下文
        const contextName = `__SANDBOX_CONTEXT_${appName.replace(/-/g, '_')}__`
        delete window[contextName]
    }

    // 清理沙箱
    clear(appName) {
        const sandbox = this.sandboxes.get(appName)
        if (sandbox) {
            // 移除 Shadow DOM 容器
            const container = sandbox.shadowRoot.host
            container.remove()
            // 清理应用特定的上下文
            const contextName = `__SANDBOX_CONTEXT_${appName.replace(/-/g, '_')}__`
            delete window[contextName]
        }
        this.sandboxes.delete(appName)
    }

    // 获取当前沙箱的 Shadow Root
    getShadowRoot(appName) {
        const sandbox = this.sandboxes.get(appName)
        return sandbox ? sandbox.shadowRoot : null
    }
}

export default new JSsandbox() 